

#include "client.h"
#include "channel.h"
#include "data_buffer.h"
#include <cerrno>
#include <cstdio>
#include "loop_event.hpp"
#include <sys/epoll.h>
#include <iostream>
#include <unistd.h>

 Client::Client(LoopEvent * loop_event)
{
    m_loop_event = loop_event;
    m_is_connection = false;
    m_data = nullptr;
}

Client::~Client()
{
    if (m_data)
    {
        delete m_data;
        m_data = nullptr;
    }
    CloseConnection();
}

void Client::Connect(const std::string &ip, uint16_t port)
{
    struct sockaddr_in sai;
    memset(&sai, 0, sizeof(sai));

    m_socket.Open();
    sai.sin_family = AF_INET;
    sai.sin_port   = htons(port);
    inet_pton(PF_INET, ip.c_str(), &sai.sin_addr);

    m_socket.SetOpt();
    m_socket.SetNonBlocking();

    int result = connect(m_socket.GetSocketFd(), (struct sockaddr*)&sai, sizeof(sai));
    (void)result;
    // std::cout << "connect result : " << result << std::endl;

    Channel * channel = nullptr;

    channel = new Channel(m_loop_event, m_socket.GetSocketFd(),
                            std::bind(&Client::WriteAble, this),
                            std::bind(&Client::ReadAble, this, std::placeholders::_1),
                            std::bind(&Client::ErrorAble, this, std::placeholders::_1, std::placeholders::_2));

    m_channel_ptr = std::shared_ptr<Channel>(channel);

    m_channel_ptr->AddEvent(EPOLLIN | EPOLLOUT | EPOLLONESHOT | EPOLLET | EPOLLERR | EPOLLRDHUP);

}

void Client::WriteAble()
{
    std::cout << __FUNCTION__ << " connection success " << std::endl;
    m_channel_ptr->UpdateEvent(EPOLLIN | EPOLLONESHOT | EPOLLET | EPOLLERR | EPOLLRDHUP);
    if (!m_is_connection)
    {
        m_is_connection = true;
        if (m_connection)
        {
            m_connection();
        }
        m_data = new DataBuffer(m_socket.GetSocketFd());
    }
}

void Client::ReadAble(int64_t channel_id)
{
    if (!m_is_connection)
    {
        m_channel_ptr->RemoveEvent();
        return;
    }
    // printf("function : %s and read able\n", __FUNCTION__);
    int status = 0;

    m_data->ReadData(status);
    if (status < 0)
    {
        ErrorAble(channel_id, errno);
    }
    else
    {
        if (m_message)
        {
            m_message(m_data->Data(), m_data->Length());
        }
        else if (m_message_with_client)
        {
            m_message_with_client(this, (const char*)m_data->Data());
        }
        m_channel_ptr->UpdateEvent(EPOLLIN | EPOLLONESHOT | EPOLLET | EPOLLERR | EPOLLRDHUP);
    }
}

void Client::ErrorAble(int64_t channel, int error_code)
{
    std::cout << __FUNCTION__  << "error code " << error_code << std::endl;
    char buf[100] = {0};
    int result = read(m_socket.GetSocketFd(), buf, sizeof(buf) - 1);
    if (result == 0)
    {
        {
            m_loop_event->PushTask([this, error_code](){

                CloseConnection();
                if (m_disconnection)
                {
                    m_disconnection(error_code);
                }
            });
        }
    }
    else
    {
        m_channel_ptr->UpdateEvent(EPOLLIN | EPOLLONESHOT | EPOLLET | EPOLLERR | EPOLLRDHUP);
    }
}

void Client::SendMessage(const std::string &content)
{
    write(m_socket.GetSocketFd(), content.c_str(), content.size());
}

void Client::SendMessage(uint8_t *data, int length)
{
    write(m_socket.GetSocketFd(), data, length);
}

void Client::CloseConnection()
{
    if (m_is_connection)
    {
        std::cout << "close client socket " << std::endl;
        m_is_connection = false;
        m_channel_ptr->RemoveEvent();
    }
    else
    {
        m_channel_ptr->RemoveEvent();
    }
}

void Client::SetOnConnection(OnConnection &&func)
{
    m_connection = func;
}

void Client::SetOnMessage(OnMessage &&func)
{
    m_message = func;
}

void Client::SetOnMessage(OnMessageWithClient &&func)
{
    m_message_with_client = func;
}

void Client::SetDisConnection(OnDisConnection &&func)
{
    m_disconnection = func;
}

int64_t Client::ChannelId()
{
    if (!m_channel_ptr)
    {
        return 0;
    }
    return m_channel_ptr->GetChannelId();
}















